////////////////////////////////////////////////////////////////////////////////
// Copyright 2010 Michael Schmalle - Teoti Graphix, LLC
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0 
// 
// Unless required by applicable law or agreed to in writing, software 
// distributed under the License is distributed on an "AS IS" BASIS, 
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and 
// limitations under the License
// 
// Author: Michael Schmalle, Principal Architect
// mschmalle at teotigraphix dot com
////////////////////////////////////////////////////////////////////////////////

package org.teotigraphix.swizplugin.core
{

import org.teotigraphix.swizplugin.api.IMemento;

/**
 * The <strong>XMLMemento</strong> is the default XML implementation 
 * of <code>IMemento</code>.
 * 
 * @author Michael Schmalle
 * @copyright Teoti Graphix, LLC 
 * @productversion 1.0
 */
public class XMLMemento implements IMemento
{
	//--------------------------------------------------------------------------
	//
	//  Internal :: Variables
	//
	//--------------------------------------------------------------------------
	
	/**
	 * @private
	 */
	private var _document:XML;
	
	/**
	 * @private
	 */	
	private var _element:XML;
	
	//--------------------------------------------------------------------------
	//
	//  Constructor
	//
	//--------------------------------------------------------------------------
	
	/**
	 * Constructor.
	 * 
	 * @param document The XML document.
	 * @param element The XML element.
	 */
	public function XMLMemento(document:XML, element:XML)
	{
		_document = document;
		_element = element;
	}
	
	//--------------------------------------------------------------------------
	//
	//  IMemento API :: Methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#createChild()
	 */
	public function createChild(type:String, id:String = null):IMemento
	{
		var child:XML = new XML("<" + type + "/>");
		if (id)
		{
			child.@id = id;
		}
		
		_element.appendChild(child);
		
		return new XMLMemento(_document, child);
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#getChild()
	 */
	public function getChild(type:String):IMemento
	{
		var nodes:XMLList = _element.children();
		var len:int = nodes.length();
		if (len == 0)
			return null;
		
		for(var i:int = 0; i < len; i++)
		{
			var node:XML = nodes[i];
			if (node.name() == type) // find the first occurence
			{
				return new XMLMemento(_document, node);
			}
		}
		
		return null;
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#getChildByID()
	 */
	public function getChildByID(id:String):IMemento
	{
		var nodes:XMLList = _element.children();
		var len:int = nodes.length();
		
		for(var i:int = 0; i < len; i++)
		{
			var node:XML = nodes[i];
			if (node.@id == id)
				return new XMLMemento(_document, node);
		}
		
		return null;
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#getChildByName()
	 */
	public function getChildByName(name:String):IMemento
	{
		var nodes:XMLList = _element.children();
		var len:int = nodes.length();
		
		for(var i:int = 0; i < len; i++)
		{
			var node:XML = nodes[i];
			if (node.@name == name)
				return new XMLMemento(_document, node);
		}
		
		return null;
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#getChildren()
	 */
	public function getChildren(type:String):Array // of IMemento
	{
		var nodes:XMLList = _element.children();
		var len:int = nodes.length();
		if (len == 0)
			return [];
		
		var list:Array = [];
		
		for(var i:int = 0; i < len; i++)
		{
			var node:XML = nodes[i];
			if (node.name() == type || type == "*") // find the first occurence
			{
				list.push(new XMLMemento(_document, node));
			}
		}
		
		return list;
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#getID()
	 */
	public function getID():String
	{
		return _element.@id;
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#getBoolean()
	 */
	public function getBoolean(key:String):Boolean
	{
		var value:String = _element.@[key];
		
		return (value == "true");
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#getFloat()
	 */
	public function getFloat(key:String):Number
	{
		var value:String = _element.@[key];
		if (value == null)
			return NaN;
		
		return Number(value);
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#getInteger()
	 */
	public function getInteger(key:String):int
	{
		var value:String = _element.@[key];
		if (!value)
			return NaN;	
			
		return int(value);
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#getString()
	 */
	public function getString(key:String):String
	{
		return String(_element.@[key]);
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#getTextData()
	 */
	public function getTextData():String
	{
		return _element.text().toString();
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#putBoolean()
	 */
	public function putBoolean(key:String, value:Boolean):void
	{
		_element.@[key] = value;
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#putFloat()
	 */
	public function putFloat(key:String, value:Number):void
	{
		_element.@[key] = value;
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#putInteger()
	 */
	public function putInteger(key:String, value:int):void
	{
		_element.@[key] = value;
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#putString()
	 */
	public function putString(key:String, value:String):void
	{
		if (value == null)
			return;
		
		_element.@[key] = value;
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#putTextData()
	 */
	public function putTextData(value:String):void
	{
		if (value == null)
			return;
		
		_element.setChildren(value);
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#putMemento()
	 */
	public function putMemento(memento:IMemento):void
	{
		putElement(XMLMemento(memento)._element, true);
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.api.IMemento#replaceMemento()
	 */
	public function replaceMemento(memento:IMemento):void
	{
		var replacement:XML = XMLMemento(memento)._element;
		// FIXME I think I might need to loop through the element's
		// attributes and copy them since I am only replacing the children
		// not the actual node
		_element.setChildren(replacement.children());
	}
	
	//--------------------------------------------------------------------------
	//
	//  Public API :: Methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 * @private
	 */
	public function putCData(value:String):void
	{
		if (value == null)
			return;
		
		_element.setChildren(cdata(value));
	}
	
	/**
	 * @private
	 */
	public function cdata(data:String):XML
	{
		return new XML("<![CDATA[" + data + "]]>");
	}
	
	/**
	 * Returns an xml representation of the memento (used for export).
	 * 
	 * @return An XML string.
	 */
	public function toXMLString():String
	{
		return _element.toXMLString();
	}	

	//--------------------------------------------------------------------------
	//
	//  Public Class :: Methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 * Creates a read root memento that loads the base xml data.
	 * 
	 * @param data A String of xml to create the root element of the memento.
	 * @return The root XML memento based off the string data.
	 */
	public static function createReadRoot(data:String):XMLMemento
	{
		var document:XML = new XML(data);
		var nodes:XMLList = document.children();
		
		return new XMLMemento(document, document);
	}
	
	/**
	 * Creates a write root starting a the type node.
	 * 
	 * @param type A String indicating the root element's type.
	 * @return The root XML memento based off the root type.
	 */
	public static function createWriteRoot(type:String):XMLMemento
	{
		var document:XML = new XML();
		var element:XML = new XML("<" + type + "/>");
		document.appendChild(element);
		
		return new XMLMemento(document, element);
	}

	//--------------------------------------------------------------------------
	//
	//  Private :: Methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 * @private
	 */	
	private function putElement(element:XML, copyText:Boolean):void
	{
		var attributes:XMLList = element.attributes(); // of XML with name() etc.
		var len:int = attributes.length();

		for(var i:int = 0; i < len; i++)
		{
			var attribute:XML = attributes[i];
			putString(attribute.name(), attribute.toString());
		}
		
		if (!copyText)
			return;
				
		var data:String = element.text().toString();
		if (data)
		{
			putTextData(data);
		}
	}	
}
}